package com.easy.threads.factory;


import com.easy.common.IAutoScan;
import com.easy.config.log.MyLog;
import com.easy.threads.actuator.AutoScan;
import com.easy.threads.actuator.Job;
import com.easy.threads.actuator.Monitor;
import com.easy.threads.entity.*;
import com.easy.threads.singleton.Constant;
import com.easy.utils.JobPoolManage;
import com.easy.utils.MaxCount;
import com.easy.utils.PartitionJobStatus;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;

/**
 * 描述: 线程创建，获取空闲现场工厂
 *
 * @author gwj
 * @date 2023年12月14日 9:19
 */

public final class ThreadFactory {

    /*
     * threadJob 集合
     */
//    private static final List<Job> JOB_LIST = new CopyOnWriteArrayList<>();
    /*
     * 任务分区管理
     */
//    private static final Map<String, List<Job>> JOB_PARTITION = new ConcurrentHashMap<>();

    /**
     * 任务分区队列管理
     */
    private static final Map<String, Integer> PARTITION_QUEUE = new ConcurrentHashMap<>();
    /**
     * 任务分区可扩容线程数量管理
     */
    private static final Map<String, Integer> PARTITION_EXPANSION_COUNT_THREAD = new ConcurrentHashMap<>();
    /**
     * 扫描线程管理
     */
    private static final Map<String, AutoScan> SCAN_BEAN_JOB = new ConcurrentHashMap<>();

    static MaxCount newMaxCount = new MaxCount(0);


    /***
     * 描述 : 创建线程池
     * @author gwj
     * @date 2024/1/5 11:19
     * @param threadCount 核心线程数量
     * @param expansionCount 可扩容数据 （核心线程不足时，可以创建的临时线程数量）
     * @param threadNamePrefix 线程名称
     * @param partition 所属分区属性
     * @param auto 是否自动扫描任务到任务池
     */
    public static void newThreadPool(int threadCount, int expansionCount, String threadNamePrefix,
                                     Partition partition, Auto auto) {
        ThreadParameter parameter = new ThreadParameter();
        parameter.setThreadCount(threadCount);
        parameter.setThreadNamePrefix(threadNamePrefix);
        parameter.setExpansionCount(expansionCount);
        parameter.setAuto(auto);
        parameter.setPartition(partition);
        createThread(parameter);
    }

    /**
     * 描述 : 创建线程池
     *
     * @param threadCount      核心线程数量
     * @param threadNamePrefix 线程名称
     * @param partition        所属分区属性
     * @param auto             是否自动扫描任务到任务池
     * @author gwj
     * @date 2024/1/5 11:21
     */
    public static void newThreadPool(int threadCount, String threadNamePrefix,
                                     Partition partition, Auto auto) {
        newThreadPool(threadCount, 0, threadNamePrefix, partition, auto);
    }

    /**
     * 描述 : 创建线程池
     *
     * @param threadCount      核心线程数量
     * @param threadNamePrefix 线程名称
     * @param partitionKey     所属分区key
     * @author gwj
     * @date 2024/1/5 11:22
     */
    public static void newThreadPool(int threadCount, String threadNamePrefix, String partitionKey) {
        newThreadPool(threadCount, threadNamePrefix, new Partition(partitionKey), null);
    }

    /**
     * 描述 : 创建线程池
     *
     * @param threadCount      核心线程数量
     * @param threadNamePrefix 线程名称
     * @author gwj
     * @date 2024/1/5 11:22
     */
    public static void newThreadPool(int threadCount, String threadNamePrefix) {
        newThreadPool(threadCount, threadNamePrefix, Constant.WORK_PARTITION_DEFAULT);
    }

    public synchronized static void createThread(ThreadParameter parameter) {

        int threadCount = parameter.getThreadCount();

        if (threadCount <= 0) {
            throw new RuntimeException("缺失关键参数，请设置线程数量");
        }
        int maxQueueLength = 500;
        String partitionKey = Constant.WORK_PARTITION_DEFAULT;
        Partition partition = parameter.getPartition();
        //本次是否扩容任务队列长度
        boolean expansion = false;
        if (partition != null) {
            maxQueueLength = partition.getMaxQueueLength();
            partitionKey = partition.getKey();
            //本次是否扩容任务队列长度
            expansion = partition.isExpansion();
        }
        String threadName = parameter.getThreadName();
        int expansionCount = parameter.getExpansionCount();
        Auto auto = parameter.getAuto();

        if (partitionKey.isEmpty()) {
            throw new IllegalArgumentException("缺失关键参数，请设置线程所属分区KEY,Partition -- > key");
        }

        //已存在当前分区的任务
//        List<Job> jobs = JOB_PARTITION.get(partitionKey);
        List<Job> jobs = JobPoolManage.getWaitJobList(partitionKey);
        if (jobs != null && !jobs.isEmpty()) {
            MyLog.getLogger().log(Level.INFO, "进入已存在分区任务:{}", partitionKey);
            int taskSize = jobs.size() + 1;
            for (int i = 0; i < threadCount; i++) {
                startJob(partitionKey,threadName + taskSize);
                taskSize++;
            }

            if (expansion) {
                Integer oldMaxQueueLength = PARTITION_QUEUE.get(partitionKey);
                int newMaxQueueLength = oldMaxQueueLength + maxQueueLength;
                //设置当前分区队列最大长度
                PARTITION_QUEUE.put(partitionKey, newMaxQueueLength);
            }
            //设置当前分区线程可扩容数据
            PARTITION_EXPANSION_COUNT_THREAD.put(partitionKey, Math.max(expansionCount, PARTITION_EXPANSION_COUNT_THREAD.get(partitionKey)));
            //判断是否开启自动扫描任务
            startSean(auto);
            return;
        }
        MyLog.getLogger().log(Level.INFO, "创建新分区:{}", partitionKey);
        //新分区任务
        int num = 1;
        for (int i = 0; i < threadCount; i++) {
//            startJob(partitionKey,threadName + num, JOB_LIST);
            startJob(partitionKey,threadName + num);
            num++;
        }
        //添加工作线程到对应分区
//        JOB_PARTITION.put(partitionKey, JOB_LIST);
        //启动监听线程
        Monitor monitor = new Monitor(partitionKey);
        monitor.setName(parameter.getThreadName() + "monitor-" + partitionKey);
        monitor.start();

        //设置当前分区队列最大长度
        PARTITION_QUEUE.put(partitionKey, maxQueueLength);
        //设置当前分区线程可扩容数据
        PARTITION_EXPANSION_COUNT_THREAD.put(partitionKey, Math.max(expansionCount, 0));
        //记录任务分区允许状态
        PartitionJobStatus.recordJobStatus(partitionKey);

        //判断是否开启自动扫描任务
        startSean(auto);
    }


    /**
     * 描述 : 启动自动扫描任务
     *
     * @param auto 依赖参数
     * @author gwj
     * @date 2023/12/15 15:54
     */
    private static void startSean(Auto auto) {
        if (auto != null) {
            //是否开启自动扫描
            boolean autoScan = auto.isStartAutoScan();
            if (autoScan) {
                IAutoScan scanBean = auto.getScanBean();
                String name = scanBean.getClass().getName();
                //校验是否存在相同的扫描执行对象
                if (!SCAN_BEAN_JOB.containsKey(name)) {
                    MyLog.getLogger().log(Level.INFO, "创建任务扫描执行类【{}】", name);
                    AutoScan scan = new AutoScan(auto);
                    SCAN_BEAN_JOB.put(name, scan);
                    scan.setName("SCAN-" + SCAN_BEAN_JOB.size());
                    scan.start();
                } else
                    MyLog.getLogger().log(Level.INFO, "任务扫描执行类【{}】已存在,不在重复创建", name);
            }
        }
    }

    /***
     * 描述 : 启动工作线程
     * @author gwj
     * @date 2023/12/15 15:52
     * @param threadName 线程名
     * @param jobList 集合
     */
    @Deprecated
    private static void startJob(String partitionKey,String threadName, List<Job> jobList) {
        MyLog.getLogger().log(Level.INFO, "创建JOB");
        Job job = new Job(partitionKey);
        job.setName(threadName);
        jobList.add(job);
        job.start();
    }
    private static void startJob(String partitionKey,String threadName) {
        MyLog.getLogger().log(Level.INFO, "创建JOB");
        Job job = new Job(partitionKey);
        job.setName(threadName);
        job.start();
    }
    /**
     * 描述 : 根据线程组key，获取空闲线程
     *
     * @param partition 任务分区key
     * @return com.easy.threads.actuator.Job
     * @author gwj
     * @date 2023/12/15 14:13
     */
    public synchronized static Job getJob(String partition) {
        Job waitJob = JobPoolManage.getWaitJob(partition);

        if(waitJob != null){
            MyLog.getLogger().log(Level.INFO, "线程名称:{},thread状态:{},job状态:{}", new Object[]{waitJob.getName(), waitJob.getState(), waitJob.getJobStatus()});
            return waitJob;
        }
       /* List<Job> jobs = JOB_PARTITION.get(partition);
        for (Job job : jobs) {
            //当前线程状态
            Thread.State state = job.getState();
            MyLog.getLogger().log(Level.INFO, "线程名称:{},thread状态:{},job状态:{}", new Object[]{job.getName(), state, job.getJobStatus()});
            //获取空闲线程
            if (WAITING.equals(state) && Constant.THREAD_STATUS_WAITING.equals(job.getJobStatus())) {
                return job;
            }
        }*/

        if(PARTITION_EXPANSION_COUNT_THREAD.get(partition)<=0){
            return null;
        }

        MyLog.getLogger().log(Level.INFO, "核心线程已满，创建临时线程,临时线程最大数为：{}", PARTITION_EXPANSION_COUNT_THREAD.get(partition));
        //判断是否大于等于最大线程数量
        boolean tempThread = newMaxCount.getCount() >= PARTITION_EXPANSION_COUNT_THREAD.get(partition);
        MyLog.getLogger().log(Level.INFO, "已存在临时线程数为：{}", newMaxCount.getCount());
        if (tempThread) {
            return null;
        }

        //新建线程
        Job job = new Job(partition);
        newMaxCount.setCount(newMaxCount.getCount() + 1);
        //设置执行一次后销毁标识
        job.setThreadFlag(Constant.THREAD_LOOP_RUN_FLAG_EXECUTE_ONCE);
        job.setName(partition+"-temp-" + newMaxCount.getCount());
        MyLog.getLogger().log(Level.INFO, "创建临时线程：{}", job.getName());
        job.start();
        return job;
    }

    /**
     * 描述 : 设置当前临时线程数量，临时线程执行结束后计数
     *
     * @author gwj
     * @date 2024/1/3 14:20
     */
    public static void reduceTempJobCount() {
        newMaxCount.setCount(newMaxCount.getCount() - 1);
        MyLog.getLogger().log(Level.INFO, "当前临时线程数据为：{}", newMaxCount.getCount());
    }

    public static String getCurrentThreadName() {
        return Thread.currentThread().getName();
    }

    public static void sleep(final int second) {
        try {
            Thread.sleep(second * 1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static int getMaxQueueLength(String partition) {

        return PARTITION_QUEUE.get(partition);

    }

    public static void killAutoScan(IAutoScan scan) {
        SCAN_BEAN_JOB.get(scan.getClass().getName()).setStart(false);
    }

    /***
     * 描述 : 停止指定分区的线程池
     * @author gwj
     * @date 2024/11/25 14:21
     * @param partition 分区标识
     */
    public boolean  stopImmediately(String partition){
       return PartitionJobStatus.stopImmediately(partition);
    }
    public boolean  taskFinishedStop(String partition){
      return PartitionJobStatus.taskFinishedStop(partition);
    }
}
